#pragma once

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <unistd.h>
#include <unordered_map>
#include <sys/types.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "Util.hpp"
#include "log.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERION "HTTP/1.0"
#define LINE_END "\r\n"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500
#define PAGE_404 "404.html"


static std::string Code2Desc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "NOt found";
        break;
    default:
        break;
    }
    return desc;
}

static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"}, 
        {".css", "text/css"}, 
        {".js", "application/javascript"}, 
        {".jpg", "application/x-jpg"}, 
        {".xml", "application/xml"}, 
    };

    auto iter = suffix2desc.find(suffix);
    if(iter != suffix2desc.end())
    {
        //找到了
        return iter->second;
    }

    return "text/html";
}

class HttpRequest //HttpRequest和HttpResponse就是用来当结构体用的
{
public:
    std::string request_line;
    std::vector<std::string> request_header;
    std::string blank;
    std::string request_body;

    // 放入解析之后的版本
    std::string method;  // 请求方法
    std::string url;     // url,
    std::string version; // 请求版本

    std::unordered_map<std::string, std::string> header_kv;
    int content_length;
    std::string path;
    std::string suffix;//后缀
    std::string query_string;
    int size; //报头的大小


    bool cgi;
public:
    HttpRequest() : content_length(0), cgi(false) {}
    ~HttpRequest() {}
};

class HttpResponse
{
public:
    std::string status_line;
    std::vector<std::string> response_header;
    std::string blank;
    std::string response_body;

    int status_code;
    int fd;

public:
    HttpResponse() :blank(LINE_END), status_code(OK), fd(-1) {}
    ~HttpResponse() {}

};

class EndPoint
{
private:
    int sock;
    bool stop;
    HttpRequest http_request;
    HttpResponse http_response;

private:
    bool RecvHttpRequestLine()
    {
        LOG(INFO, "Begin RecvHttpRequestLine");     //http_request和http_response就是两个结构体，本来就没用东西，后续会把得到的请求和构建的相应放进去，最后返回
        auto &line = http_request.request_line;
        std::cout << "-----------------------------" << http_request.request_line << "///////////" <<line << std::endl;

        if (Util::ReadLine(sock, line) > 0)
        {
            line.resize(line.size() - 1);
            LOG(INFO, http_request.request_line);
        }
        else
        {
            stop = true;
        }
        return stop;
    }

    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            if(Util::ReadLine(sock, line) <= 0)
            {
                stop = true;
                break;
            }

            if (line == "\n")   //空行，说明可以解析完了
            {
                http_request.blank = line;
                break;
            }
            line.resize(line.size() - 1); // 去掉\n
            http_request.request_header.push_back(line);
            LOG(INFO, line);
        }
        return stop;
    }

    void ParseHttpRequestLine()
    {
        auto &line = http_request.request_line;

        std::stringstream ss(line);
        ss >> http_request.method >> http_request.url >> http_request.version;
        auto &method = http_request.method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper); //固定转换成大写,因为是自己写的协议，别人不一定完全遵守

        std::cout << "-----------------------------" << http_request.method << "///////////" << std::endl;
        std::cout << "-----------------------------" << method << "///////////" << std::endl;
        LOG(INFO, http_request.method);
        LOG(INFO, http_request.url);
        LOG(INFO, http_request.version);
        std::cout << "解析完请求了11111111111111111  173行" << std::endl;
    }

    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for(auto &iter : http_request.request_header)
        {
            if (Util::CutString(iter, key, value, SEP))
            {
                http_request.header_kv.insert({key, value}); // 可以直接使用花括号插入
            }
        }
    }
    bool IsNeedRecvHttpRequestBody() // 在第10节12min处
    {
        auto &method = http_request.method;
        if (method == "POST")
        {
            auto &header_kv = http_request.header_kv;
            auto iter = header_kv.find("Content -Length");
            if (iter != header_kv.end())
            {
                http_request.content_length = atoi(iter->second.c_str());
                return true;
            }
        }
        return false;
    }

    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = http_request.content_length;
            auto &body = http_request.request_body;

            char ch = 0;
            while (content_length)
            {
                ssize_t s = recv(sock, &ch, 1, 0);
                if (s > 0)
                {
                    body.push_back(ch);
                    content_length--;
                }
                else
                {
                    stop = true;
                    break;
                }
            }
            LOG(INFO, body);
        }
        return stop;
    }



    int ProcessCgi()//CGI模式
    {
        std::cout << "debug: " << "Usage CGI MOKEL" << std::endl;

        int code = OK;
        //父进程数据
        auto &method = http_request.method;
        auto &query_string = http_request.query_string;
        auto &body_text = http_request.request_body;
        auto &bin = http_request.path;  //要让子进程执行的目标程序
        int content_length = http_request.content_length;
        auto& response_body = http_response.response_body;
        
        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        
        int input[2];
        int output[2];

        if(pipe(input) < 0)
        {
            //创建失败
            LOG(ERROR, "pipe input error!");
            code = SERVER_ERROR;
            return code;
        }
        if(pipe(output) < 0)
        {
            LOG(ERROR, "pipe outpue error!");
            code = SERVER_ERROR;
            return code;
        }
         
        pid_t pid = fork();
        if(pid == 0)
        {
            //子进程   
            close(input[0]); //只关闭读端口，往input里写
            close(output[1]); //只关闭写端口，只读

            //重定向，写出-> 1 ->input[1]
            //        读入-> 0 ->output[0]
            //此时就不需要知道fd了，只要读0写1就行
           

            //子进程是不知道自己是该在标准输入里读还是在环境变量里读的，所以也要把方法传给子进程

            method_env = "METHOD=";
            method_env += method;
            putenv((char*)method_env.c_str());

            if(method == "GET")
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char*)query_string_env.c_str());
                LOG(INFO, "Get Method, Add Query_String Env");
            }
            else if(method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char*)content_length_env.c_str());
                LOG(INFO, "Post Method, Add Content_Length Env");
            }
            else{ //do no thing
            }

            //最后再重定向，否则LOG信息就会被打到文件里
            dup2(output[0], 0);
            dup2(input[1], 1);
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);//不要再往后走了
        }
        else if(pid < 0)
        {
            //error
            LOG(ERROR, "fork error!");
            return 404;
        }
        else
        {
            //父进程
            close(input[1]);
            close(output[0]);

            if(method == "POST")
            {
                const char* start = body_text.c_str();
                int total = 0;
                int size = 0;
                while((total < content_length) && (size = write(output[1], start + total, body_text.size()-total)) > 0)  //判断是否写完，写完了就停
                {
                    total += size;
                }
            }

            //父进程要获得子进程的结果
            char ch = 0;
            while(read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }

            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if(ret == pid)
            {
                //读取出错
                if(WIFEXITED(status))
                {
                    if(WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else code = BAD_REQUEST;
                }
                else code = SERVER_ERROR;
            }
            close(input[0]);
            close(output[1]);
        }

        return code;
    }

    int ProcessNoCgi() // 简单的网页请求，返回静态网页
    {
        // 在NoCgi模式下，肯定是正常的网页请求
        http_response.fd = open(http_request.path.c_str(), O_RDONLY);
        if (http_response.fd >= 0)
        {
            return OK;
        }

        return NOT_FOUND;
    }

    void HandlerError(std::string page)
    {
        //返回出错页面
        http_request.cgi = false;
        http_response.fd = open(page.c_str(), O_RDONLY);
        if(http_response.fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            http_request.size =st.st_size;

            std::string line = "Content-Type: test/html";
            line += LINE_END;
            http_response.response_header.push_back(line);
            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line +=LINE_END;
            http_response.response_header.push_back(line);

        }
    }

    void BuildOkResponse()
    {

        std::string line = "Content-Type: ";
        line += Suffix2Desc(http_request.suffix);
        line += LINE_END;
        http_response.response_header.push_back(line);
        
        line = "Content-Length: ";
        if(http_request.cgi)
        {
            line += std::to_string(http_response.response_body.size());
        }
        else{
            line += std::to_string(http_request.size);//get方法
        }
        line += LINE_END;
        http_response.response_header.push_back(line);
    }


    void BuildHttpResponseHelper()
    {   
        
        auto &code = http_response.status_code;
        
        //构建状态行
        auto &status_line = http_response.status_line;
        status_line += HTTP_VERION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;

        
        //构建响应正文
        std::string path = WEB_ROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        case BAD_REQUEST:
            path += PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            path += PAGE_404;
            HandlerError(path);
            break;
        default:
            break;
        }
    }


public:
    EndPoint(int _sock):sock(_sock), stop(false) {}

    bool  IsStop()
    {
        return stop;
    }

    void RecvHttpRequest()
    {
        LOG(INFO, "解析Http请求中");
        if ((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))
        {
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
            RecvHttpRequestBody();
        }
    }

    void BuildHttpResponse()
    {
        std::string _path;
        auto& code = http_response.status_code;
        struct stat st;
        //int size = 0;//默认的文件大小为0
        std::size_t found = 0;
        if(http_request.method != "GET" && http_request.method != "POST")
        {
            //非法请求
            std::cout << "method: " << http_request.method << std::endl;
            LOG(WARRING, "method is not right");
            code = BAD_REQUEST;
            goto END;
        }
        if(http_request.method == "GET")
        {
            size_t pos = http_request.url.find('?');
            if(pos != std::string::npos)
            {
                Util::CutString(http_request.url, http_request.path, http_request.query_string, "?");
                http_request.cgi = true;
            }
            else
            {
                http_request.path = http_request.url;
            }    
        }
        else if(http_request.method == "POST")
        {
            //上传，cgi模式
            http_request.cgi = true;
            http_request.path = http_request.url;//你不仅仅要保证是cgi模式，同时也要把执行路径给它
        }
        else {} //什么都不做
        //测试
        // std::cout << "Debug:url " << http_request.url << std::endl;
        // std::cout << "Debug:path " << http_request.path << std::endl;
        // std::cout << "Debug:query_string " << http_request.query_string << std::endl;
        
        
        //拼接上我们的web根目录
         _path = http_request.path;
        http_request.path = WEB_ROOT;
        http_request.path += _path;

        //std::cout << "debug: path:" << http_request.path << std::endl;
        if(http_request.path[http_request.path.size() -1] == '/')   //没有指定访问的资源，就返回首页
        {
            http_request.path += HOME_PAGE;
        }
        //std::cout << "Debug: " << http_request.path << std::endl;

        
        if(stat(http_request.path.c_str(), &st) == 0)
        {
            //资源存在
            if(S_ISDIR(st.st_mode))
            {
                //说明是目录，访问目录也是不允许的，需要做下处理
                //虽然是目录，但是不是以/结尾
                http_request.path += "/";
                http_request.path += HOME_PAGE;
                stat(http_request.path.c_str(), &st); //16节11min
            }

            if((st.st_mode&S_IXUSR) || (st.st_mode&S_IXOTH) || (st.st_mode&S_IXGRP))
            {
                //可执行文件，特殊处理
                http_request.cgi = true;

            }
            //size = st.st_size();//说明文件存在，拿到他的文件大小
            //size = st.st_size;//说明文件存在，拿到他的文件大小
            http_request.size = st.st_size;
        }
        else
        {
            //资源不存在
            //std::string info = http_request.path;
            //info += " Not found!";
            LOG(WARNING, http_request.path + " NOT FOUND");
            code = NOT_FOUND;
            goto END;
        }

        //path
        found = http_request.path.rfind(".");
        if(found == std::string::npos)
        {
            //没找到
            http_request.suffix = ".html";
        }
        else
        {
            //找到了
            http_request.suffix = http_request.path.substr(found);
        }


        //走到这里说明不是正常的网页请求就是cgi模式
        if(http_request.cgi)
        {
            code = ProcessCgi();//CGI模式
        }
        else
        {
           code = ProcessNoCgi();//简单的网页请求,返回静态网页,只需要打开就行
        }

END:
        
            //构建相应失败
            BuildHttpResponseHelper();
    }

    void SendHttpResponse()
    {
        send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
        for(auto iter : http_response.response_header)
        {
            send(sock, iter.c_str(), iter.size(), 0);
        }
        send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);
        

        //fd, response_body
        if(http_request.cgi)
        {
            auto& response_body = http_response.response_body;
            size_t size = 0;
            size_t total = 0;
            const char* start = response_body.c_str();
            while((total < response_body.size()) && (size = send(sock, start+total, response_body.size()-total, 0)) > 0)
            {
                total += size;
            }
        }
        else
        {
            std::cout << ".............." << http_response.fd << std::endl;
            std::cout << ".............." << http_request.size << std::endl;
            sendfile(sock, http_response.fd, nullptr, http_request.size);
            close(http_response.fd); // 关闭打开的fd
        }
    }

    ~EndPoint() 
    { 
        close(sock); 
    }
};



//#define DEBUG 1

class CallBack
{
public:

    CallBack()
    {}

    void operator()(int sock)
    {
        HandlerRequest(sock);
    }

     void HandlerRequest(int sock)
    {
        LOG(INFO, "Hander Request Begin");

#ifdef DEBUG
        // 只是用来测试
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << "----------------------begin---------------------" << std::endl;
        std::cout << buffer << std::endl;
        std::cout << "----------------------end-----------------------" << std::endl;
#else
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();//读取请求


        if((!ep->IsStop()))
        {
            LOG(INFO, "Recv No Error, Begin Build And Send");

            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else{
            LOG(WARNING, "Recv Error, Stop Build And Send");
        }
        delete ep;

#endif
        LOG(INFO, "Hander Request End");
    }

    ~CallBack()
    {}
};
